// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Matrix3Test.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Test.Robotics.Numerics
{
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Xml;
    using System.Xml.Serialization;

    using Microsoft.Robotics.Numerics;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Unit tests for the Matrix3 class
    /// </summary>
    [TestClass]
    public class Matrix3Test
    {
        /// <summary>
        /// Size of the square matrix under test
        /// </summary>
        private const int MatrixSize = 3;

        /// <summary>
        /// Verifies the default constructor functionality.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3DefaultConstructor()
        {
            Matrix3 matrix = new Matrix3();
            Assert.IsTrue(
                matrix[0, 0] == 0.0 && matrix[0, 1] == 0.0 && matrix[0, 2] == 0.0 && matrix[1, 0] == 0.0
                && matrix[1, 1] == 0.0 && matrix[1, 2] == 0.0 && matrix[2,  0] == 0.0 && matrix[2,  1] == 0.0
                && matrix[2,  2] == 0.0, 
                "Matrix3 instance does not have its elements set to the default value");
        }

        /// <summary>
        /// Verifies the copy constructor functionality.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3CopyConstructor()
        {
            Matrix3 matrixToCopy = new Matrix3();
            matrixToCopy[0, 0] = 11.0;
            matrixToCopy[0, 1] = 12.0;
            matrixToCopy[0, 2] = 13.0;
            matrixToCopy[1, 0] = 21.0;
            matrixToCopy[1, 1] = 22.0;
            matrixToCopy[1, 2] = 23.0;
            matrixToCopy[2,  0] = 31.0;
            matrixToCopy[2,  1] = 32.0;
            matrixToCopy[2,  2] = 33.0;

            Matrix3 newCopyMatrix = new Matrix3(matrixToCopy);

            Assert.IsTrue(
                newCopyMatrix[0, 0].CompareTo(11.0) == 0 && newCopyMatrix[0, 1].CompareTo(12.0) == 0
                && newCopyMatrix[0, 2].CompareTo(13.0) == 0 && newCopyMatrix[1, 0].CompareTo(21.0) == 0
                && newCopyMatrix[1, 1].CompareTo(22.0) == 0 && newCopyMatrix[1, 2].CompareTo(23.0) == 0
                && newCopyMatrix[2,  0].CompareTo(31.0) == 0 && newCopyMatrix[2,  1].CompareTo(32.0) == 0
                && newCopyMatrix[2,  2].CompareTo(33.0) == 0, 
                "At least one element from the new instance differs from the matrix to copy");
        }

        /// <summary>
        /// Verifies construction from a 2D  array
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3ConstructFrom2DArray()
        {
            double[,] m1 = new double[3, 3];
            m1[0, 0] = 11.0;
            m1[0, 1] = 12.0;
            m1[0, 2] = 13.0;
            m1[1, 0] = 21.0;
            m1[1, 1] = 22.0;
            m1[1, 2] = 23.0;
            m1[2,  0] = 31.0;
            m1[2,  1] = 32.0;
            m1[2,  2] = 33.0;

            Matrix3 m2 = new Matrix3(m1);

            Assert.AreEqual(m2[0, 0], m1[0, 0]);
            Assert.AreEqual(m2[0, 1], m1[0, 1]);
            Assert.AreEqual(m2[0, 2], m1[0, 2]);
            Assert.AreEqual(m2[1, 0], m1[1, 0]);
            Assert.AreEqual(m2[1, 1], m1[1, 1]);
            Assert.AreEqual(m2[1, 2], m1[1, 2]);
            Assert.AreEqual(m2[2, 0], m1[2, 0]);
            Assert.AreEqual(m2[2, 1], m1[2, 1]);
            Assert.AreEqual(m2[2, 2], m1[2, 2]);
        }

        /// <summary>
        /// Serialization / Deserialization
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3Serialize()
        {
            Matrix3 m1 = new Matrix3();
            m1[0, 0] = 11.0;
            m1[0, 1] = 12.0;
            m1[0, 2] = 13.0;
            m1[1, 0] = 21.0;
            m1[1, 1] = 22.0;
            m1[1, 2] = 23.0;
            m1[2,  0] = 31.0;
            m1[2,  1] = 32.0;
            m1[2,  2] = 33.0;

            using (MemoryStream sr = new MemoryStream(2048))
            {
                DataContractSerializer ds = new DataContractSerializer(typeof(Matrix3));

                XmlWriter xw = XmlWriter.Create(sr);
                ds.WriteObject(xw, m1);
                xw.Flush();

                sr.Position = 0;

                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(sr, new XmlDictionaryReaderQuotas());
                Matrix3 m2 = (Matrix3)ds.ReadObject(reader, true);
                Assert.IsTrue(m1 == m2, "Serialization round trip failed");
            }
        }

        /// <summary>
        /// Verifies the copy constructor functionality.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3CopyConstructorNullArgument()
        {
            Matrix3 matrixToCopy = null;

            Matrix3 newCopyMatrix = new Matrix3(matrixToCopy);
        }

        /// <summary>
        /// Verifies the constructor set the expected value in all elements of the matrix.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3DefaultValueConstructor()
        {
            const double DefaultValue = 42.0;
            Matrix3 valueMatrix = new Matrix3(DefaultValue);
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    Assert.IsTrue(
                        valueMatrix[i, j].CompareTo(DefaultValue) == 0, 
                        string.Format("Element [{0}, {1}] is not equal to expected value of {2}", i, j, DefaultValue));
                }
            }
        }

        /// <summary>
        /// Verifies that setting and getting each elements using the indexer leads
        /// the the same value. 
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3ElementsIndexerValid()
        {
            Matrix3 matrix = new Matrix3();

            double startValue = 1.0;
            double expectedValue;
            int counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrix[i, j] = counter * startValue;
                }
            }

            counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    expectedValue = counter * startValue;
                    Assert.IsTrue(
                        matrix[i, j].CompareTo(expectedValue) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            matrix[i, j], 
                            expectedValue));
                    matrix[i, j] = counter * startValue;
                }
            }
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsIndexerSetterInvalid()
        {
            Matrix3 matrix = new Matrix3();

            matrix[0, MatrixSize + 1] = 42.0;
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsFirstIndexerSetterInvalid()
        {
            Matrix3 matrix = new Matrix3();

            matrix[MatrixSize + 1, 0] = 42.0;
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsNegativeFirstIndexerSetterInvalid()
        {
            Matrix3 matrix = new Matrix3();

            matrix[-MatrixSize, 0] = 42.0;
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsNegativeSecondIndexerSetterInvalid()
        {
            Matrix3 matrix = new Matrix3();

            matrix[0, -MatrixSize] = 42.0;
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsIndexerGetterInvalid()
        {
            Matrix3 matrix = new Matrix3();
            double foo = matrix[0, MatrixSize + 1];
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsNegativeSecondIndexerGetterInvalid()
        {
            Matrix3 matrix = new Matrix3();
            double foo = matrix[0, -MatrixSize];
        }

        /// <summary>
        /// Verifies an exception is thrown when using the indexer with an invalid index
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void Matrix3ElementsNegativeFirstIndexerGetterInvalid()
        {
            Matrix3 matrix = new Matrix3();
            double foo = matrix[-MatrixSize, 0];
        }

        /// <summary>
        /// Verifies the return from the Identity method is a valid identity matrix
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3IdentityMatrixIsValid()
        {
            Matrix3 matrix = Matrix3.Identity();
            double expectedValue;

            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    if (i == j)
                    {
                        expectedValue = 1.0;
                    }
                    else
                    {
                        expectedValue = 0.0;
                    }

                    Assert.IsTrue(
                        matrix[i, j].CompareTo(expectedValue) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            matrix[i, j], 
                            expectedValue));
                }
            }
        }

        /// <summary>
        /// Verifies the matrix gets properly transposed
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3TransposeMatrixIsValid()
        {
            Matrix3 matrix = new Matrix3();

            double startValue = 1.0;
            double expectedValue;
            int counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrix[i, j] = counter * startValue;
                }
            }

            // Original matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            Matrix3 transposed = Matrix3.Transpose(matrix);

            // Transposed matrix should be:
            // 1, 4, 7
            // 2,  5, 8
            // 3, 6, 9
            counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    expectedValue = counter * startValue;
                    Assert.IsTrue(
                        transposed[j, i].CompareTo(expectedValue) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            transposed[i, j], 
                            expectedValue));
                }
            }
        }

        /// <summary>
        /// Verifies the proper exception is thrown is a null matrix is provided
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3TransposeMatrixNullArgument()
        {
            Matrix3 matrix = null;

            Matrix3 transposed = Matrix3.Transpose(matrix);

            Assert.IsTrue(transposed == null, "Returned transposed matrix should be null");
        }

        /// <summary>
        /// Verifies the product of 2 valid Matrix3 matrices returns the expected result.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3Multiplication()
        {
            Matrix3 matrixLeft = new Matrix3();
            int counter = 0;

            // Left matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrixLeft[i, j] = counter * 1.0;
                }
            }

            Matrix3 matrixRight = new Matrix3();

            // Right matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrixRight[i, j] = counter * 1.0;
                }
            }

            // Expected product matrix should be:
            // (1*1 + 4*2 + 7*3), (2*1 + 5*2 + 8*3), (3*1 + 6*2 + 9*3)    (1+8+21) , (2+10+24) , (3+12+27)     30,  36,  42
            // (1*4 + 4*5 + 7*6), (2*4 + 5*5 + 8*6), (3*4 + 6*5 + 9*6) == (4+20+42), (8+25+48) , (12+30+54) == 66,  81,  96
            // (1*7 + 4*8 + 7*9), (2*7 + 5*8 + 8*9), (3*7 + 6*8 + 9*9)    (7+32+63), (14+40+72), (21+48+81)    102,  126, 150
            Matrix3 expectedMatrix = new Matrix3();
            expectedMatrix[0, 0] = 30;
            expectedMatrix[0, 1] = 36;
            expectedMatrix[0, 2] = 42;
            expectedMatrix[1, 0] = 66;
            expectedMatrix[1, 1] = 81;
            expectedMatrix[1, 2] = 96;
            expectedMatrix[2,  0] = 102;
            expectedMatrix[2,  1] = 126;
            expectedMatrix[2,  2] = 150;

            Matrix3 productMatrix = matrixLeft * matrixRight;

            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    Assert.IsTrue(
                        productMatrix[i, j].CompareTo(expectedMatrix[i, j]) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            productMatrix[i, j], 
                            expectedMatrix[i, j]));
                }
            }
        }

        /// <summary>
        /// Verifies that a Matrix3 multiplied with a null Matrix3 instance returns a null
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3MultiplicationRightNullArgument()
        {
            Matrix3 matrixLeft = new Matrix3();
            Matrix3 matrixRight = null;

            Matrix3 productMatrix = matrixLeft * matrixRight;
        }

        /// <summary>
        /// Verifies that a Matrix3 multiplied with a null Matrix3 instance returns a null
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3MultiplicationLeftNullArgument()
        {
            Matrix3 matrixRight = new Matrix3();
            Matrix3 matrixLeft = null;

            Matrix3 productMatrix = matrixLeft * matrixRight;
        }

        /// <summary>
        /// Verifies a Matrix3 multiplied by a fixed multiplier returns the expected values.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3Multiply()
        {
            Matrix3 matrix = new Matrix3();
            int counter = 0;

            // Matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrix[i, j] = counter * 1.0;
                }
            }

            Matrix3 expectedMatrix = new Matrix3();
            expectedMatrix[0, 0] = 2.0;
            expectedMatrix[0, 1] = 4.0;
            expectedMatrix[0, 2] = 6.0;
            expectedMatrix[1, 0] = 8.0;
            expectedMatrix[1, 1] = 10.0;
            expectedMatrix[1, 2] = 12.0;
            expectedMatrix[2,  0] = 14.0;
            expectedMatrix[2,  1] = 16.0;
            expectedMatrix[2,  2] = 18.0;

            Matrix3 multiplied = 2.0 * matrix;

            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    Assert.IsTrue(
                        multiplied[i, j].CompareTo(expectedMatrix[i, j]) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            multiplied[i, j], 
                            expectedMatrix[i, j]));
                }
            }
        }

        /// <summary>
        /// Verifies that a null Matrix3 multiplied with a constant multiplier returns a null
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3MultiplyNullArgument()
        {
            Matrix3 matrix = null;

            Matrix3 multiplied = 42.0 * matrix;

            Assert.IsTrue(multiplied == null, "Returned product matrix should be null");
        }

        /// <summary>
        /// Verifies a Vector3 multiplied by a Matrix3 returns the expected Vector3 values
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3Vector3Multiply()
        {
            Vector3 vector = new Vector3(1.0, 2.0, 3.0);

            Matrix3 matrix = new Matrix3();
            int counter = 0;

            // Matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrix[i, j] = counter * 1.0;
                }
            }

            // Expected vector should be
            // X = 1*1 + 2*4 + 3*7 = 1 + 8 + 21 = 30
            // Y = 1*2 + 2*5 + 3*8 = 2 + 10 + 24 = 36
            // Z = 1*3 + 2*6 + 3*9 = 3 + 12 + 27 = 42
            Vector3 expectedVector = new Vector3(30.0, 36.0, 42.0);

            Vector3 multiplied = vector * matrix;

            Assert.IsTrue(
                multiplied.X.CompareTo(expectedVector.X) == 0, 
                string.Format(
                    "Value X from multiplied vector {0} is not equal to expected value of {1}", 
                    multiplied.X, 
                    expectedVector.X));
            Assert.IsTrue(
                multiplied.Y.CompareTo(expectedVector.Y) == 0, 
                string.Format(
                    "Value Y from multiplied vector {0} is not equal to expected value of {1}", 
                    multiplied.Y, 
                    expectedVector.Y));
            Assert.IsTrue(
                multiplied.Z.CompareTo(expectedVector.Z) == 0, 
                string.Format(
                    "Value Z from multiplied vector {0} is not equal to expected value of {1}", 
                    multiplied.Z, 
                    expectedVector.Z));
        }

        /// <summary>
        /// Verifies that a null Matrix3 multiplied with Vector3 returns a null
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3Vector3MultiplyNullArgument()
        {
            Vector3 vector = new Vector3(1.0, 2.0, 3.0);

            Matrix3 matrix = null;

            Vector3 multiplied = vector * matrix;
        }

        /// <summary>
        /// Verifies that null arguments are handled correctly
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3Vector3AdditionRightNullArgument()
        {
            Matrix3 leftSide = new Matrix3();
            Matrix3 rightSide = new Matrix3();
            Matrix3 result = null;

            rightSide = null;
            result = leftSide + rightSide;
        }

        /// <summary>
        /// Verifies that null arguments are handled correctly
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3Vector3AdditionLeftNullArgument()
        {
            Matrix3 leftSide = new Matrix3();
            Matrix3 rightSide = new Matrix3();
            Matrix3 result = null;

            leftSide = null;
            result = leftSide + rightSide;
        }

        /// <summary>
        /// Verifies the sum of 2 valid Matrix3 matrices returns the expected result.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3AdditionOperator()
        {
            Matrix3 matrixLeft = new Matrix3();

            // Left matrix is:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            int counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    matrixLeft[i, j] = counter * 1.0;
                }
            }

            Matrix3 matrixRight = new Matrix3();

            // Left matrix is:
            // 9, 8, 7
            // 6, 5, 4
            // 3, 2,  1
            counter = 9;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    matrixRight[i, j] = counter * 1.0;
                    counter--;
                }
            }

            Matrix3 matrixSum = matrixLeft + matrixRight;

            double expectedValue = 10.0;

            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    Assert.IsTrue(
                        matrixSum[i, j].CompareTo(expectedValue) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            matrixSum[i, j], 
                            expectedValue));
                }
            }
        }

        /// <summary>
        /// Verifies the difference of Matrix3 matrices returns the expected result.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3RightNullSubstractionOperator()
        {
            Matrix3 matrixLeft = new Matrix3();
            Matrix3 matrixRight = null;
            Matrix3 result = matrixLeft - matrixRight;
        }

        /// <summary>
        /// Verifies the difference Matrix3 matrices returns the expected result.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3LeftNullSubstractionOperator()
        {
            Matrix3 matrixRight = new Matrix3();
            Matrix3 matrixLeft = null;
            Matrix3 result = matrixLeft - matrixRight;
        }

        /// <summary>
        /// Verifies the difference of 2 valid Matrix3 matrices returns the expected result.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3SubstractionOperator()
        {
            Matrix3 matrixLeft = this.CreateFilledInMatrix(0.0);
            Matrix3 matrixRight = new Matrix3();

            // Right matrix is:
            // 9, 8, 7
            // 6, 5, 4
            // 3, 2,  1
            int counter = 9;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    matrixRight[i, j] = counter * 1.0;
                    counter--;
                }
            }

            Matrix3 matrixDifference = matrixLeft - matrixRight;

            Matrix3 expectedMatrix = new Matrix3();
            expectedMatrix[0, 0] = -8.0;
            expectedMatrix[0, 1] = -6.0;
            expectedMatrix[0, 2] = -4.0;
            expectedMatrix[1, 0] = -2.0;
            expectedMatrix[1, 1] = 0.0;
            expectedMatrix[1, 2] = 2.0;
            expectedMatrix[2,  0] = 4.0;
            expectedMatrix[2,  1] = 6.0;
            expectedMatrix[2,  2] = 8.0;

            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    Assert.IsTrue(
                        matrixDifference[i, j].CompareTo(expectedMatrix[i, j]) == 0, 
                        string.Format(
                            "Element [{0}, {1}]'s value {2} is not equal to expected value of {3}", 
                            i, 
                            j, 
                            matrixDifference[i, j], 
                            expectedMatrix[i, j]));
                }
            }
        }

        /// <summary>
        /// Verifies the equality operator returns true on 2 identical matrices
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3EqualOperatorNull()
        {
            Matrix3 leftSide = new Matrix3();
            Matrix3 rightSide = new Matrix3();
            rightSide = null;
            Assert.IsFalse(leftSide == rightSide, "Matrix3 equals operator right side null test failed");
            rightSide = new Matrix3();
            leftSide = null;
            Assert.IsFalse(leftSide == rightSide, "Matrix3 equals operator left side null test failed");
        }

        /// <summary>
        /// Verifies the equality operator returns true on 2 identical matrices
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3EqualOperatorSame()
        {
            Matrix3 matrixLeft = this.CreateFilledInMatrix(0.0);
            Matrix3 matrixRight = this.CreateFilledInMatrix(0.0);

            Assert.IsTrue(matrixLeft == matrixRight, "The equality operator returned false on two identical matrices");
        }

        /// <summary>
        /// Verifies the equality operator returns false on 2 different matrices
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3EqualOperatorDifferent()
        {
            Matrix3 matrixLeft = this.CreateFilledInMatrix(0.0);
            Matrix3 matrixRight = this.CreateFilledInMatrix(10.0);

            Assert.IsFalse(matrixLeft == matrixRight, "The equality operator returned true on two different matrices");
        }

        /// <summary>
        /// Verifies right multiplication by a vector - null case.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Matrix3RightNullMultiplyOperator()
        {
            Matrix3 matrix = null;
            Vector3 rightVec = new Vector3();
            Vector3 result = matrix * rightVec;
        }

        /// <summary>
        /// Verifies right vector multiplication - success case
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3RightMultiplyOperator()
        {
            Matrix3 zeroMatrix = new Matrix3();

            // Create matrix:
            // 1, 2,  3
            // 4, 5, 6
            // 7, 8, 9
            Matrix3 matrix = this.CreateFilledInMatrix(0.0);

            // Case 1 Zero matrix
            Vector3 rightVec = new Vector3(1.0, 2.0, 3.0);
            Vector3 result = zeroMatrix * rightVec;
            Assert.AreEqual(result.X, 0.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Y, 0.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Z, 0.0, MathConstants.ErrorEpsilon);

            // Case 2 Zero right vector
            Vector3 zeroVec = new Vector3(0.0, 0.0, 0.0);
            result = matrix * zeroVec;
            Assert.AreEqual(result.X, 0.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Y, 0.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Z, 0.0, MathConstants.ErrorEpsilon);

            // Case 3 Generic
            result = matrix * rightVec;
            Assert.AreEqual(result.X, 14.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Y, 32.0, MathConstants.ErrorEpsilon);
            Assert.AreEqual(result.Z, 50.0, MathConstants.ErrorEpsilon);
        }

        /// <summary>
        /// Tests the Hashcode method
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3GetHashCode()
        {
            Matrix3 matrix = this.CreateFilledInMatrix(0.0);
            matrix[0, 0] = 0.0;
            int result = matrix.GetHashCode();
            Assert.IsTrue(result != 0, "Matrix3 hash code is zero");
        }

        /// <summary>
        /// Tests the overridden equals method
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3EqualityMethod()
        {
            Matrix3 leftSide = this.CreateFilledInMatrix(0.0);
            Matrix3 rightSide = this.CreateFilledInMatrix(0.0);
            Assert.IsTrue(leftSide.Equals(rightSide), "Matrix3 equals method failed");
            int foo = 0;
            Assert.IsFalse(leftSide.Equals(foo), "Matrix3 equals method retuned wrong result for unlike objects");
            rightSide = this.CreateFilledInMatrix(10.0);
            Assert.IsFalse(leftSide.Equals(rightSide), "Matrix3 equals method for non-equal matrices");
        }

        /// <summary>
        /// Test the Matrix inverse
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Matrix3Inverse()
        {
            // case 1: Zero matrix
            Matrix3 mat = new Matrix3();
            Matrix3 matInv = mat.Inverse();
            Assert.IsNull(matInv);

            // case 2: Degenerate non zero matrix
            mat = this.CreateFilledInMatrix(0.0);
            matInv = mat.Inverse();
            Assert.IsNull(matInv);

            // case 3: Matrix is no longer degenerate
            mat[0, 0] = mat[0, 0] + 3.0;
            mat[0, 1] = mat[0, 0] + 1.2;
            matInv = mat.Inverse();
            Matrix3 prod = mat * matInv;
            this.CheckForUnitMatrix(prod);

            // case 4: generic matrix
            Random rand = new Random(543);
            for (int i = 0; i < 3; ++i)
            {
                for (int j = 0; j < 3; ++j)
                {
                    mat[i, j] = rand.NextDouble();
                }
            }

            matInv = mat.Inverse();
            prod = mat * matInv;
            this.CheckForUnitMatrix(prod);
        }

        /// <summary>
        /// Checks if argument is a unit matrix
        /// </summary>
        /// <param name="mat">Matrix to Check</param>
        private void CheckForUnitMatrix(Matrix3 mat)
        {
            for (int i = 0; i < 3; ++i)
            {
                for (int j = 0; j < 3; ++j)
                {
                    if (i == j)
                    {
                        Assert.AreEqual(mat[i, j], 1.0, MathConstants.ErrorEpsilon);
                    }
                    else
                    {
                        Assert.AreEqual(mat[i, j], 0.0, MathConstants.ErrorEpsilon);
                    }
                }
            }
        }

        /// <summary>
        /// Helper method to create a filled in matrix
        /// </summary>
        /// <param name="offset">Offset each number my this value</param>
        /// <returns>Filled in matrix</returns>
        private Matrix3 CreateFilledInMatrix(double offset)
        {
            Matrix3 result = new Matrix3();
            int counter = 0;
            for (int i = 0; i < MatrixSize; i++)
            {
                for (int j = 0; j < MatrixSize; j++)
                {
                    counter++;
                    result[i, j] = (counter * 1.0) + offset;
                }
            }

            return result;
        }
    }
}
